<template>
  <section>
    <component :is="component"
               multiple="multiple"
               :accept="accept"
               :action="computedAction"
               :headers="headers"
               :data="handleUploadData"
               :beforeUpload="beforeUpload"
               :remove="remove"
               :fileList="fileList"
               :showUploadList="showUploadList"
               :configGroup="configGroup"
               :bucketName="bucketName"
               :disabled="disabled || hasDefaultFile"
               :listType="listType"
               :systemName="systemName"
               @change="handleChange"
               @preview="handlePreview">
      <template v-if="showButton">
        <template v-if="!onlyImage">
          <a-button v-if="!draggable"
                    :style="style"
                    :type="type"
                    :disabled="disabled">
            <a-icon type="upload" />
            文件上传
          </a-button>
          <template v-else>
            <p class="ant-upload-drag-icon">
              <a-icon type="inbox" />
            </p>
            <p class="ant-upload-text">
              点击或拖拽文件到此区域
            </p>
          </template>
        </template>
        <template v-else>
          <a-icon type="plus" />
          <div class="ant-upload-text">
            点击上传
          </div>
        </template>
      </template>
      <a-tooltip v-if="placeholder">
        <template slot="title">
          {{ placeholder }}
        </template>
        <template>
          <span class="placeholder">?</span>
        </template>
      </a-tooltip>
    </component>
    <a-modal :visible="previewVisible"
             :footer="null"
             @cancel="previewVisible=false">
      <img alt="example"
           style="width: 100%"
           :src="previewImage" />
    </a-modal>
  </section>
</template>

<script>

import { Modal } from 'ant-design-vue'

import PropTypes from '@zh/common-utils/prop-types'

import { storage } from '@zh/common-utils/http'

import { getFileInfoList } from '../api/file'

import { toBase64 } from '@zh/common-utils/file/utils'

import mimeType from '@zh/common-utils/file/mime-type'

import { shuffle } from 'lodash'

export default {
  name: 'ZFileUploader',
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    placeholder: PropTypes.string.def(''),

    // 文件展现方式
    listType: PropTypes.string.def('text'),

    // 组件标签
    component: PropTypes.string.def('a-upload'),

    value: PropTypes.array.def([]),

    disabled: PropTypes.bool.def(undefined),

    showUploadList: PropTypes.bool.def(true),

    // 自定义上传函数
    beforeUploadFn: PropTypes.func.def(undefined),

    // 自定义上传函数
    uploadFn: PropTypes.func.def(undefined),

    // 自定义删除文件
    beforeDeleteFn: PropTypes.func.def(undefined),

    // 接收文件类型，多个类型用逗号分隔
    accept: PropTypes.string.def(''),

    // 显示上传按钮
    readonly: PropTypes.bool.def(false),

    buttonVisible: PropTypes.bool.def(true),

    // 上传数量限制
    limit: PropTypes.number.def(undefined),

    // 文件大小限制
    max: PropTypes.number.def(200),

    // 是否在设计器中渲染
    inDesigner: PropTypes.bool.def(false),

    // 是否只上传图片
    onlyImage: PropTypes.bool.def(false),

    // 是否有默认值
    hasDefaultFile: PropTypes.bool.def(false),

    buttonType: PropTypes.string.def(undefined),

    // 自定义预览方法
    preview: PropTypes.func.def(undefined),

    headers: PropTypes.object.def({}),
    configGroup: PropTypes.string.require(),
    bucketName: PropTypes.string.require(),
    systemName: PropTypes.string.require()
  },
  data () {
    return {
      storage,
      previewVisible: false,
      previewImage: '',
      fileList: [],
      limitSize: this.max * 1024 * 1024
    }
  },
  computed: {
    style () {
      if (this.buttonType?.startsWith('#')) {
        if (this.disabled) {
          return null
        } else {
          return { 'background-color': this.buttonType, color: '#fff' }
        }
      }
      return null
    },
    type () {
      return this.buttonType != 'secondary' ? this.buttonType : null
    },
    draggable () {
      return this.component == 'a-upload-dragger'
    },
    /**
     * 设计界面始终显示操作按钮
     * 非设计界面如果只读或被禁用或达到数量限制则不显示操作按钮
     */
    showButton () {
      if (this.inDesigner === true) {
        return true
      }
      if (this.hasDefaultFile) {
        // 已有默认值则不显示按钮
        return false
      }
      if (this.limit) {
        return this.fileList?.length < this.limit
      }
      return this.readonly !== false || this.disabled !== false
    },
    computedAction () {
      return this.uploadFn ? undefined : storage.uploadURL
    }
  },
  watch: {
    value: {
      handler (value) {
        if (value?.length > 0 && this.fileList.length == 0) {
          this.getFileInfoList()
        }
      },
      deep: true,
      immediate: true
    }
  },
  mounted () {
  },
  methods: {
    getFileInfoList () {
      getFileInfoList(this.value).then(response => {
        const keymap = {}
        this.fileList = response.map(item => {
          item = { ...item, url: `${storage.downloadURL}/${item.uid}`, _id: item.uid }
          if (keymap[item.uid]) {
            item.uid = shuffle(item.uid)
          }
          return item
        })
        this.$emit('change', this.fileList.map(file => file._id), this.fileList)
      })
    },
    handleChange (options) {
      const idMap = {}
      this.fileList.map(file => {
        file._id && (idMap[file.uid] = file._id)
      })
      this.fileList = options.fileList.map(file => {
        const item = {
          uid: file.uid,
          name: file.name,
          status: file.status,
          configGroup: this.configGroup,
          url: file.url
        }
        idMap[file.uid] && (item._id = idMap[file.uid])

        if (file.response) {
          if (file.response?.code == 0) {
            item.url = `${storage.downloadURL}/${file.response.data.key}`
            item._id = file.response.data.key
            item.status = 'done'
          } else {
            this.$notify.error(file.response.msg)
            item.status = 'error'
          }
        }
        return item
      })
      if (options.file.status == 'done' || options.file.status == 'removed') {
        this.$emit('change', this.fileList.filter(file => file._id && !file._id.startsWith('vc-upload')).map(file => file._id), this.fileList)
      }
    },
    /**
     * 上传前验证文件大小及类型
     */
    beforeUpload (file) {
      if (this.beforeUploadFn) {
        return this.beforeUploadFn()
      }
      return new Promise((resolve, reject) => {
        if (file.size > this.limitSize) {
          this.$notify.error(`上传文件不能大于 ${this.max}MB`)
          return
        }
        const suffix = file.name.toLowerCase().split('.').pop()
        if (this.accept && !this.accept.includes(suffix)) {
          this.$notify.error(`不支持上传该文件类型`)
          return
        }
        resolve()
      })
    },
    remove (file) {
      if (this.beforeDelete) {
        return this.beforeDelete(file)
      }
      return new Promise((resolve, reject) => {
        Modal.confirm({
          title: '提示',
          content: '删除将会立即生效,确认删除吗？',
          destroyOnClose: true,
          okText: '确定',
          okType: 'danger',
          cancelText: '取消',
          onOk: () => {
            resolve()
          }
        })
      })
    },
    handleUploadData (file) {
      return {
        uid: file.uid,
        name: file.name,
        status: 'done',
        chunked: false,
        bucketName: this.bucketName,
        systemName: this.systemName
      }
    },
    /**
     * 预览
     */
    handlePreview (file) {
      if (this.preview) {
        this.preview(file)
      }
      if (!file.url && !file.preview) {
        file.preview = toBase64(file.originFileObj)
      }
      const suffix = file.name.toLowerCase().split('.').pop()

      if (mimeType.image.value.includes(suffix)) {
        // 图片预览
        this.previewImage = file.url || file.preview
        this.previewVisible = true
      } else {
        // 非图片则下载
        window.open(file.url)
      }
    }
  }
}
</script>
<style lang="less" scoped>
.placeholder {
  cursor: default;
  display: inline-block;
  width: 16px;
  height: 16px;
  background: #8a8a8a;
  color: #fff;
  border-radius: 50%;
  line-height: 16px;
  text-align: center;
  margin-left: 5px;
  font-size: var(--font-size-12);
}
</style>
